package com.jcfk.system.controller;

import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.jcfk.common.business.enums.AvailableStatus;
import com.jcfk.common.business.log.annotation.RequestLogHeader;
import com.jcfk.common.business.utils.DataBindHelper;
import com.jcfk.common.core.utils.IdUtils;
import com.jcfk.common.security.annotation.AllowAnonymous;
import com.jcfk.common.web.domain.DataPage;
import com.jcfk.common.web.domain.ReqPageParam;
import com.jcfk.common.web.domain.RetPage;
import com.jcfk.common.web.domain.RetResult;

import com.jcfk.system.domain.dto.AnnexesFileDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import com.jcfk.system.domain.query.SysAnnexesFileQuery;
import com.jcfk.system.domain.dto.SysAnnexesFileDTO;
import com.jcfk.system.service.SysAnnexesFileService;
import org.springframework.web.multipart.MultipartFile;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;

/**
 * 文件中心 服务接口
 *
 * @author libinghai
 * @email
 * @date 2023-12-13
 */
@Api(tags = {"文件中心"})
@RestController
@RequestMapping("fileCenter")
public class AnnexesFileCenterController {
    @Autowired
    private SysAnnexesFileService sysAnnexesFileService;

    private static final java.util.List<String> ALLOWED_FILE_TYPES = Arrays.asList("txt", "csv", "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "jpg", "jpeg", "glb", "gif", "png", "bmp", "avi", "mp4", "mov", "MKV", "wmv", "flv", "mpeg", "webm", "glb");

    /**
     * 分页列表
     */
    @ApiImplicitParams({@ApiImplicitParam(paramType = "body", dataType = "ReqPageParam<SysAnnexesFileQuery>", name = "requestParam", value = "", required = true)})
    @ApiOperation("分页查询附件信息")
    @PostMapping("/queryByPage")
    public RetPage<SysAnnexesFileDTO> page(@RequestBody ReqPageParam<SysAnnexesFileQuery> requestParam) {
        DataPage<SysAnnexesFileDTO> page = new DataPage<SysAnnexesFileDTO>((long) requestParam.getCurrent(), (long) requestParam.getSize(), requestParam.getSort().getOrderItem(), SysAnnexesFileDTO.class);
        page = sysAnnexesFileService.queryByPage(page, requestParam.getData());

        return new RetPage<>(page);
    }
    // endregion

    /**
     * 获取文件信息列表
     *
     * @param query
     * @return
     */
    @PostMapping("/getList")
    public RetResult<?> getList(@RequestBody SysAnnexesFileQuery query) {
        List<SysAnnexesFileDTO> list = sysAnnexesFileService.getList(query);

        return RetResult.ok(list);
    }

    // region 附件信息

    /**
     * 附件信息
     * 根据id 查询详情
     */
    @ApiOperation("查询附件信息")
    @GetMapping("/get/{id}")
    public RetResult<SysAnnexesFileDTO> info(@PathVariable("id") String id) {
        SysAnnexesFileDTO item = sysAnnexesFileService.get(id);
        return RetResult.ok(item);
    }
    // endregion


    // region 保存附件信息

    /**
     * 保存附件信息
     */
    @ApiOperation("保存附件信息")
    @PostMapping("/save")
    @RequestLogHeader(modular = "附件信息管理", businessType = "保存")
    public RetResult<?> save(@RequestBody @Valid SysAnnexesFileDTO sysAnnexesFile) {
        // 数据校验
        RetResult ret = sysAnnexesFileService.isValid(sysAnnexesFile);
        if (!ret.getCode().equals(RetResult.SUCCESS)) {
            return ret;
        }

        if (StrUtil.isEmpty(sysAnnexesFile.getId())) {
            // 新增
            sysAnnexesFileService.insert(sysAnnexesFile);
        } else {
            // 更新
            sysAnnexesFileService.update(sysAnnexesFile);
        }
        return RetResult.ok();

    }
    // endregion

    // region 批量删除附件信息

    /**
     * 批量删除附件信息
     */
    @ApiOperation("批量删除附件信息")
    @PostMapping("/deleteList")
    @RequestLogHeader(modular = "附件信息管理", businessType = "批量删除")
    public RetResult<?> deleteList(@RequestBody SysAnnexesFileDTO sysAnnexesFile) {
        return RetResult.ok(sysAnnexesFileService.removeBigBatch(sysAnnexesFile.getIds()));
    }
    // endregion

    /**
     * 存储路径
     */
    @Value("${file.storagePath:}")
    private String storagePath;

    // region 上传文件

    /**
     * 上传文件
     *
     * @param folder1 文件夹1
     * @param file    文件
     * @return
     * @throws Exception
     */
    @PostMapping("uploadFile/{folder1}")
    @AllowAnonymous
    public RetResult<?> uploadFile(@PathVariable(value = "folder1") String folder1, @RequestParam(value = "file") MultipartFile file) throws Exception {

        // 保存路径
        String path = folder1;

        return doUploadFile(path, file);
    }

    /**
     * 上传文件
     *
     * @param folder1 文件夹1
     * @param folder2 文件夹2
     * @param file    文件
     * @return
     * @throws Exception
     */
    @PostMapping("uploadFile/{folder1}/{folder2}")
    @AllowAnonymous
    public RetResult<?> uploadFile(@PathVariable(value = "folder1") String folder1, @PathVariable(value = "folder2") String folder2, @RequestParam(value = "file") MultipartFile file) throws Exception {

        // 保存路径
        String path = folder1 + "/" + folder2;

        return doUploadFile(path, file);
    }

    /**
     * 上传文件
     *
     * @param folder1 文件夹1
     * @param folder2 文件夹2
     * @param folder3 文件夹3
     * @param file    文件
     * @return
     * @throws Exception
     */
    @PostMapping("uploadFile/{folder1}/{folder2}/{folder3}")
    @AllowAnonymous
    public RetResult<?> uploadFile(@PathVariable(value = "folder1") String folder1, @PathVariable(value = "folder2") String folder2, @PathVariable(value = "folder3") String folder3, @RequestParam(value = "file") MultipartFile file) throws Exception {

        // 保存路径
        String path = folder1 + "/" + folder2 + "/" + folder3;

        return doUploadFile(path, file);
    }
    // endregion

    // region 执行上传

    /**
     * 执行上传
     *
     * @param path 文件路径
     * @param file 文件
     * @return
     * @throws Exception
     */
    private RetResult<?> doUploadFile(String path, MultipartFile file) throws Exception {
        // 文件名
        String originalFilename = file.getOriginalFilename();
        // 扩展名
        String fileExtensions = FileUtil.getSuffix(originalFilename);

        // 获取上传的文件信息
        if (file == null) {
            // 返回
            return RetResult.fail("服务端未检测到上传文件信息");
        }

        if (!ALLOWED_FILE_TYPES.contains(fileExtensions.toLowerCase())) {
            // 返回
            return RetResult.fail("上传的文件格式不合法");
        }

        AnnexesFileDTO annexesFile = new AnnexesFileDTO();

        String filePath = storagePath + "/" + path;

        String saveFileName = IdUtils.snowflakeId().toString() + "." + fileExtensions;
        if (!FileUtil.exist(filePath)) {
            FileUtil.mkdir(filePath);
        }

        file.transferTo(new File(filePath + "/" + saveFileName));

        // 设置文件信息
        annexesFile.setOriginalFilename(originalFilename);
        annexesFile.setFileName(saveFileName);
        annexesFile.setFileType(fileExtensions);
        annexesFile.setPath(path);
        annexesFile.setUrl(path + "/" + saveFileName);

        // 返回
        return RetResult.ok(annexesFile);
    }
    // endregion

    // region 下载文件

    /**
     * 下载文件
     *
     * @param response
     * @param folder1          文件夹1
     * @param originalFilename 原始文件名
     * @param fileName         存储文件名
     * @throws Exception
     */
    @AllowAnonymous
    @GetMapping("download/{folder1}/{fileName}")
    public void download(HttpServletResponse response, @PathVariable(value = "folder1") String folder1, @PathVariable(value = "fileName") String fileName, @RequestParam(required = false) String originalFilename) throws Exception {

        String path = folder1;

        doDownload(response, path, fileName, originalFilename);
    }

    /**
     * 下载文件
     *
     * @param response
     * @param folder1          文件夹1
     * @param folder2          文件夹2
     * @param originalFilename 原始文件名
     * @param fileName         存储文件名
     * @throws Exception
     */
    @AllowAnonymous
    @GetMapping("download/{folder1}/{folder2}/{fileName}")
    public void download(HttpServletResponse response, @PathVariable(value = "folder1") String folder1, @PathVariable(value = "folder2") String folder2, @PathVariable(value = "fileName") String fileName, @RequestParam(required = false) String originalFilename) throws Exception {

        String path = folder1 + "/" + folder2;

        doDownload(response, path, fileName, originalFilename);
    }

    /**
     * 下载文件
     *
     * @param response
     * @param folder1          文件夹1
     * @param folder2          文件夹2
     * @param folder3          文件夹3
     * @param originalFilename 原始文件名
     * @param fileName         存储文件名
     * @throws Exception
     */
    @AllowAnonymous
    @GetMapping("download/{folder1}/{folder2}/{folder3}/{fileName}")
    public void download(HttpServletResponse response, @PathVariable(value = "folder1") String folder1, @PathVariable(value = "folder2") String folder2, @PathVariable(value = "folder3") String folder3, @PathVariable(value = "fileName") String fileName, @RequestParam(required = false) String originalFilename) throws Exception {

        String path = folder1 + "/" + folder2 + "/" + folder3;

        doDownload(response, path, fileName, originalFilename);
    }

    // endregion

    // region 执行下载

    private void doDownload(HttpServletResponse response, String path, String fileName, String originalFilename) throws Exception {
        File file = new File(storagePath + "/" + path + "/" + fileName);

        if (!file.exists()) {
            throw new RuntimeException("文件不存在");
        }

        if (StrUtil.isEmpty(originalFilename)) {
            originalFilename = fileName;
        }

        FileReader fileReader = new FileReader(file);

        // 将文件写入输入流
        byte[] buffer = FileUtil.readBytes(file);

        // 清空response
        response.reset();
        // 设置response的Header
        response.setCharacterEncoding("UTF-8");
        // Content-Disposition的作用：告知浏览器以何种方式显示响应返回的文件，用浏览器打开还是以附件的形式下载到本地保存
        // attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
        // filename表示文件的默认名称，因为网络传输只支持URL编码的相关支付，因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(originalFilename, "UTF-8"));
        // 告知浏览器文件的大小
        response.addHeader("Content-Length", "" + file.length());
        OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
        response.setContentType("application/octet-stream");
        outputStream.write(buffer);
        outputStream.flush();
    }

    // endregion

    // region 查看文件

    /**
     * 查看文件
     *
     * @param response
     * @param folder1  文件夹
     * @param filename 文件
     * @param width    图片宽度(可选)
     * @throws Exception
     */
    @AllowAnonymous
    @GetMapping("preview/{folder1}/{filename}")
    public void preview(HttpServletResponse response, @PathVariable(value = "folder1") String folder1, @PathVariable(value = "filename") String filename, @RequestParam(required = false) Integer width) throws Exception {

        String path = folder1;

        doPreview(response, path, filename, width);
    }

    @AllowAnonymous
    @GetMapping("preview/{folder1}/{folder2}/{filename}")
    public void preview(HttpServletResponse response, @PathVariable(value = "folder1") String folder1, @PathVariable(value = "folder2") String folder2, @PathVariable(value = "filename") String filename, @RequestParam(required = false) Integer width) throws Exception {

        String path = folder1 + "/" + folder2;

        doPreview(response, path, filename, width);
    }

    @AllowAnonymous
    @GetMapping("preview/{folder1}/{folder2}/{folder3}/{filename}")
    public void preview(HttpServletResponse response, @PathVariable(value = "folder1") String folder1, @PathVariable(value = "folder2") String folder2, @PathVariable(value = "folder3") String folder3, @PathVariable(value = "filename") String filename, @RequestParam(required = false) Integer width) throws Exception {

        String path = folder1 + "/" + folder2 + "/" + folder3;

        doPreview(response, path, filename, width);
    }


    private void doPreview(HttpServletResponse response, String path, String filename, Integer width) throws Exception {
        String fileExtensions = FileUtil.getPrefix(filename);

        // 设置contentType
        if (fileExtensions.toLowerCase().equals("png")) {
            response.setContentType(MediaType.IMAGE_PNG_VALUE);
        }
        if (fileExtensions.toLowerCase().equals("jpg") || fileExtensions.toLowerCase().equals("jpeg")) {
            response.setContentType(MediaType.IMAGE_JPEG_VALUE);
        }
        if (fileExtensions.toLowerCase().equals("gif") || fileExtensions.toLowerCase().equals("gif")) {
            response.setContentType(MediaType.IMAGE_GIF_VALUE);
        }
        if (fileExtensions.toLowerCase().equals("pdf")) {
            response.setContentType(MediaType.APPLICATION_PDF_VALUE);
        }
        // 获取outputStream
        ServletOutputStream outputStream = response.getOutputStream();
        if (!"null".equals(String.valueOf(width))) {
            BufferedImage bufferedImage = ImgUtil.read(storagePath + "/" + path + "/" + filename);
            // bufferedImage.getType();
            // 获取图片宽度
            int imageWidth = bufferedImage.getWidth();
            // 计算缩放比例
            Float ratio = (float) width / imageWidth;
            Image scaleImage = ImgUtil.scale(bufferedImage, ratio);

            byte[] fileBytes = ImgUtil.toBytes(scaleImage, "jpg");
            // 输出字节流
            IoUtil.write(outputStream, true, fileBytes);
        } else {
            byte[] fileBytes = FileUtil.readBytes(storagePath + "/" + path + "/" + filename);
            // 输出字节流
            IoUtil.write(outputStream, true, fileBytes);
        }
    }
}
